package com.zh.webcommon.common.query.jpa;

import com.zh.webcommon.common.auth.LoginUserHolder;
import com.zh.webcommon.common.param.CommonParam;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 实体的查询条件处理的抽象类
 *
 * @param <T>
 */
@Slf4j
public abstract class AbstractConditionStrategy<T,Q extends CommonParam> implements ConditionStrategy<T> {
    static final String FILED_CREATE_TIME = "createTime";
    static final String FIELD_DELETED = "deleted";
    static final String FIELD_TENANT_ID = "tenantId";

    private final Q condition;

    protected AbstractConditionStrategy(Q condition) {
        this.condition = condition;
    }

    @Override
    public Pageable getPage() {
        return PageRequest.of(condition().getNum() - 1, condition().getSize());
    }

    @Override
    public Specification<T> getSpecification() {
        //空条件
        return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
            //构造公共查询条件
            List<Predicate> list = new ArrayList<>();

            specification(new Param<>(root, query, cb), list);

            //查询条件值
            LocalDateTime startTime = condition.getStartTime();
            LocalDateTime endTime = condition.getEndTime();
            if (startTime != null && endTime != null) {
                list.add(cb.between(root.get(FILED_CREATE_TIME), startTime, endTime));
            }

            try {
                list.add(cb.equal(root.get(FIELD_TENANT_ID), LoginUserHolder.tenantId()));
            } catch (Exception ex) {
                log.info("字段{} 未成功赋值. msg: {}", FIELD_TENANT_ID, ex.getMessage());
            }

            try {
                list.add(cb.equal(root.get(FIELD_DELETED), false));
            } catch (Exception ex) {
                log.info("字段{} 未成功赋值. msg: {}", FIELD_DELETED, ex.getMessage());
            }

            //排序（方式二）
            if (StringUtils.hasText(condition.getSortField())) {
                query.orderBy(condition.isAsc() ? cb.asc(root.get(condition.getSortField())) : cb.desc(root.get(condition.getSortField())));
            }

            return cb.and(list.toArray(new Predicate[0]));
        };
    }

    /**
     * 构造特别的查询条件
     * 通用的条件已经构造了，无需重复，
     * 特别的查询条件，需要子类去实现构造
     */
    protected abstract void specification(Param<T> meta, List<Predicate> predicates);


    public final Q condition(){
        return condition;
    }

    /**
     * jpa的条件查询元数据
     * @param <T>
     */
    protected static class Param<T> {
        public final Root<T> root;
        public final CriteriaQuery<?> query;
        public final CriteriaBuilder cb;

        public Param(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            this.root = root;
            this.query = query;
            this.cb = cb;
        }
    }
}
